package no.ntnu.fp.gui.dialog;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.sql.Date;
import java.sql.Time;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.ImageIcon;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

import no.ntnu.fp.client.Client;
import no.ntnu.fp.gui.EventDispatcher;
import no.ntnu.fp.gui.events.AcceptNoticeEvent;
import no.ntnu.fp.gui.events.AddMeetingEvent;
import no.ntnu.fp.gui.events.AddOrUpdateAppointmentEvent;
import no.ntnu.fp.gui.events.ChangeMeetingEvent;
import no.ntnu.fp.gui.events.GuiEvent;
import no.ntnu.fp.gui.events.RejectNoticeEvent;
import no.ntnu.fp.gui.mainFrame.ModelListenerProvider;
import no.ntnu.fp.messages.impl.AddOrUpdateAppointmentMessage.UpdateType;
import no.ntnu.fp.model.Appointment;
import no.ntnu.fp.model.DialogModel.StateOfDialog;
import no.ntnu.fp.model.Meeting;
import no.ntnu.fp.model.MeetingRoom;
import no.ntnu.fp.model.Notice;
import no.ntnu.fp.model.events.DialogChangeEvent;
import no.ntnu.fp.model.events.ModelEvent;
import no.ntnu.fp.model.listeners.ModelListener;
import no.ntnu.fp.utils.MeetingRoomSchedule;
import no.ntnu.fp.utils.Participant;
import no.ntnu.fp.utils.TimeInterval;

/**
 * @author Hï¿½kon Heggedal
 * oppretter en av dialogene:
 *  - nytt mï¿½te/avtale     - ingen input
 *  - endre mï¿½te		   - mï¿½te inn 
 *  - endre avtale		   - avtale inn (eier selv)
 *  - godta/avslï¿½ avtale   - avtale inn
 */
@SuppressWarnings("serial")
public class DialogFrame extends JDialog implements ModelListenerProvider {


	private GridBagConstraints c1, c2, c3;
	private ImageIcon iconImage;

	// visning for deltakere:
	private AttendingRenderer personRenderer;

	// paneler som kan vï¿½re med
	private Shared top;
	private Deltakere participants;
	private AcceptOrDecline acceptOrDecline;
	private SaveOrCancel saveOrCancel;
	private StateOfDialog state;
	private Appointment appointment;
	private Notice notice;
	/**
	 * nytt mï¿½te / avtale
	 */
	public DialogFrame(final JFrame parent, Calendar calendar) {
		super(parent, true);
		this.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
		setLayout(new GridBagLayout());

		// ikon pï¿½ vinduet :)
		iconImage = new ImageIcon("images/newEvent3.png");
		setIconImage(iconImage.getImage());

		personRenderer = new AttendingRenderer();

		c1 = new GridBagConstraints();
		c1.gridx = 0;
		c1.gridy = 0;
		c1.fill = GridBagConstraints.VERTICAL;

		c2 = new GridBagConstraints();
		c2.gridx = 0;
		c2.gridy = 1;
		c2.fill = GridBagConstraints.VERTICAL;

		c3 = new GridBagConstraints();
		c3.gridx = 0;
		c3.gridy = 2;
		c3.fill = GridBagConstraints.VERTICAL;

		top = new Shared(calendar);
		top.setRadioButtonListeners(new AppointmentRadioButtonListener(),
				new MeetingRadioButtonListener());

		participants = new Deltakere();
		personRenderer = new AttendingRenderer();
		participants.setListCellRenderer(personRenderer);
		saveOrCancel = new SaveOrCancel();
		saveOrCancel.addButtonListeners(new AcceptButtonPushed(), 
				new CancelButtonPressedListener());
		acceptOrDecline = new AcceptOrDecline();

		add(top, c1,0);
		add(participants, c2, 1);
		add(saveOrCancel, c3,2);
		add(acceptOrDecline, c3,2);

		this.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e){
				if(DialogFrame.this.state != StateOfDialog.INVITED){
					setVisible(false);
				}else{
					JOptionPane.showMessageDialog(DialogFrame.this, 
							"Du mÃ¥ svare pÃ¥ mÃ¸te inkallingen fÃ¸r du kan " +
							"lukke dette vinduet", "MÃ¸teinkalling", 
							JOptionPane.INFORMATION_MESSAGE);
				}
			}
		});
	}

	private void createAppointmentOrMeeting(){
		this.setTitle("Lag ny avtale eller mÃ¸te");
		saveOrCancel.setSubmitText("Lagre hendelse");
		top.setEditable(true);
		top.setMeeting(false);
		top.setVisible(true);
		saveOrCancel.setVisible(true);
	}

	private void editMeeting(Meeting m){		
		setTitle("Endre mï¿½te");
		top.setCurrentAppointment(m);
		top.setEditable(true);

		top.setTitle(m.getTitle());
		top.setMeeting(true);
		top.setDescription(m.getDescription());
		top.setDate(m.getDate());
		top.setStartTime(m.getStartTime());
		top.setEndTime(m.getEndTime());
		top.disableType();
		saveOrCancel.setSubmitText("Endre og Varsle");

		top.setPlaceSelected(!(m.getPlace() == null || "".equals(m.getPlace())));
		top.setMeetingRoomSelected(m.getPlace() == null || "".equals(m.getPlace()));
		top.setPlaceText(m.getPlace());

		participants.setParticipants(m.getParticipants());
		participants.setEditable(true);
		
		participants.setVisible(true);
		top.setVisible(true);
		saveOrCancel.setVisible(true);
	}
	private void editAppointment(Appointment a){
		setTitle("Endre avtale");
		top.setCurrentAppointment(a);
		top.setEditable(true);

		top.setTitle(a.getTitle());
		top.setMeeting(false);
		top.setDescription(a.getDescription());
		top.setDate(a.getDate());
		top.setStartTime(a.getStartTime());
		top.setEndTime(a.getEndTime());

		top.setPlaceSelected(!(a.getPlace() == null || "".equals(a.getPlace())));
		top.setMeetingRoomSelected(a.getPlace() == null || "".equals(a.getPlace()));
		top.setPlaceText(a.getPlace());
		//		top.setMeetingRoom(a.getRoom());

		top.disableType();

		saveOrCancel.setSubmitText("Lagre endringer");

		top.setVisible(true);
		saveOrCancel.setVisible(true);
	}
	private void viewAppointmentOrMeeting(Appointment a){
		if(a instanceof Meeting){
			editMeeting((Meeting) a);
		}else{
			editAppointment(a);
		}
		top.setEditable(false);
		participants.setEditable(false);
		saveOrCancel.setVisible(false);
	}
	private void acceptOrDecline(Meeting m){
		setTitle("Invitert til møte");
		top.setEditable(false);
		participants.setEditable(false);
		top.setCurrentAppointment(m);

		top.setTitle(m.getTitle());
		top.setMeeting(true);
		top.setDescription(m.getDescription());
		top.setDate(m.getDate());
		top.setStartTime(m.getStartTime());
		top.setEndTime(m.getEndTime());
		top.setPlaceText(m.getPlace());

		top.setPlaceSelected(!(m.getPlace() == null || "".equals(m.getPlace())));
		top.setMeetingRoomSelected(m.getPlace() == null || "".equals(m.getPlace()));

		top.setVisible(true);
		participants.setVisible(true);
		saveOrCancel.setSubmitText("Godta invitasjon");
		saveOrCancel.setCancelText("AvslÃ¥ invitasjon");
		saveOrCancel.setVisible(true);
		participants.setParticipants(m.getParticipants());
		
		//		acceptOrDecline.setVisible(true);
		//participants.setParticipants(participants); //list<Participant>

		/*
		if(deny is chosen){
			acceptOrDecline.setDenied()
		}
		else if(accepted is chosen){
			acceptOrDecline.setAccepted()
		}

		 */

	}
	private void hideAllComponents(){
		top.clear();
		top.setVisible(false);
		participants.clear();
		participants.setVisible(false);
		saveOrCancel.setVisible(false);
		saveOrCancel.setCancelText("Avbryt");
		acceptOrDecline.setVisible(false);
	}

	private final class AcceptButtonPushed implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			GuiEvent event = null;
			switch (state) {
			case APPOINTMENT:
			case OWN_MEETING:
				boolean meeting = top.isMeetingSelected();
				boolean newAppointment = (appointment == null);
				event = createAddOrUpdateAppointmentEvent(meeting, newAppointment);

				break;
			case INVITED:
				event = new AcceptNoticeEvent(DialogFrame.this, notice);
				break;
				//				
				//			case OWN_MEETING:
				//				event = new ChangeMeetingEvent(DialogFrame.this, meeting)
				//				break;

			default:
				throw new UnsupportedOperationException(
						"Unsupported dialog state: " + state);
			}
			if (event != null) {
				EventDispatcher.dispatchEvent(event);
				setVisible(false);
			}
		}


		public GuiEvent createAddOrUpdateAppointmentEvent(boolean meeting, 
				boolean newAppointment) {
			String title = top.getTitle();
			String description = top.getDescription();
			String place = top.getPlace();
			MeetingRoom meetingRoom = top.getMeetingRoom();
			Date date = top.getDate();
			Time startTime = top.getStartTime();
			Time endTime = top.getEndTime();

			long id = (newAppointment ? -1 : appointment.getId());

			if (!meeting) {
				Appointment appointment = new Appointment(id, place, title, 
						description, meetingRoom, Client.getEmployee(), startTime, 
						endTime, date);
				UpdateType type = (newAppointment ? UpdateType.ADD
						: UpdateType.UPDATE);
				if (isValid(appointment)) {
					return new AddOrUpdateAppointmentEvent(DialogFrame.this, 
							appointment, type);
				} else {
					
					//TODO: Error handling
					JOptionPane.showMessageDialog(DialogFrame.this, 
							"Ugyldig avtale - velg tidsintervall eller " +
							"møterom på nytt");
					
					return null;
				}
			} else {
				List<Participant> participantList = participants.getParticipants();
				Meeting theMeeting = new Meeting(id, place, title, description, 
						meetingRoom, Client.getEmployee(), startTime, endTime, 
						date, false, participantList);
				if (isValid(theMeeting)) {
					return (newAppointment 
							? new AddMeetingEvent(DialogFrame.this, theMeeting)
					: new ChangeMeetingEvent(DialogFrame.this, theMeeting));
				} else {
					
					//TODO: Error handling
					JOptionPane.showMessageDialog(DialogFrame.this, 
							"Ugyldig avtale - velg tidsintervall eller " +
							"møterom på nytt");
					
					return null;
				}
			}
		}

		private boolean isValid(Appointment appointment) {
			MeetingRoomSchedule schedule = top.getSchedule();
			Time endTime = appointment.getEndTime();
			Time startTime = appointment.getStartTime();
			Date date = appointment.getDate();
			TimeInterval interval = new TimeInterval(date, startTime, endTime);
			MeetingRoom room = appointment.getRoom();
			return startTime.before(endTime)
			&& (room == null 
				||schedule.isAvailable(room, interval));
		}
	}

	private final class CancelButtonPressedListener implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			if (state != StateOfDialog.INVITED) {
				DialogFrame.this.setVisible(false);
			} else {
				EventDispatcher.dispatchEvent(
						new RejectNoticeEvent(this, notice));
				setVisible(false);
			}
		}
	}

	private final class AppointmentRadioButtonListener implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			saveOrCancel.setSubmitText("Lagre avtale");
			participants.setVisible(false);
			pack();
		}
	}

	private final class MeetingRadioButtonListener implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			participants.setVisible(true);
			participants.setEditable(true);
			switch (state) {
			case APPOINTMENT:
				saveOrCancel.setSubmitText("Lagre og inviter");
				break;
			case OWN_MEETING:
				saveOrCancel.setSubmitText("Lagre og varsle");
				break;
			}
			pack();
		}
	}

	private class DialogChangeListener implements ModelListener<DialogChangeEvent> {
		@Override
		public void handleEvent(DialogChangeEvent event) {
			//Ny dialog opprettet her

			/*
			 * DialogChangeEventen skal inneholde en getType som indikerer 
			 * hvorvidt dette er ï¿½nske om ï¿½ opprette/endre mï¿½te/avtale,
			 * og en getAppointment/meeting for ï¿½ hente objektet
			 */

			appointment = event.getNewAppointment();

			hideAllComponents();
			top.setDates();
			state = event.getNewState();
			switch (state) {
			case APPOINTMENT:  // ny appoinment eller gammel appointment
				if(event.getNewAppointment() == null){
					DialogFrame.this.createAppointmentOrMeeting();
				}
				else{
					if (appointment instanceof Meeting) {
						DialogFrame.this.editMeeting((Meeting)appointment);
					} else {
						DialogFrame.this.editAppointment(appointment);
					}
				}

				break;
			case INVITED: // invasjon til mï¿½te
				notice = event.getNotice();
				DialogFrame.this.acceptOrDecline((Meeting)event.getNewAppointment());
				break;
			case OWN_MEETING: // gammelt mï¿½te
				DialogFrame.this.editMeeting((Meeting) event.getNewAppointment() );
				break;
			case MEETING:
				DialogFrame.this.viewAppointmentOrMeeting(event.getNewAppointment());
				break;
			};


			DialogFrame.this.pack();
			DialogFrame.this.setLocationRelativeTo(DialogFrame.this.getParent());
			DialogFrame.this.validate();
			DialogFrame.this.repaint();
			DialogFrame.this.setVisible(true);
		}
	}

	@Override
	public Map<Class<? extends ModelEvent>, ModelListener<?>> getEventListeners() {

		Map<Class<? extends ModelEvent>, ModelListener<?>> map =
			new HashMap<Class<? extends ModelEvent>, ModelListener<?>>();

		map.put(DialogChangeEvent.class, new DialogChangeListener());
		map.putAll(top.getEventListeners());

		return map;
	}

}